﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GerbilLib;
using System.Net;
using System.Threading.Tasks;
using System.IO;

namespace MediaGerbil
{
    public partial class FileDownload : Form
    {
        private Uri fileUri = null;
        private Task<long> filesizeTask = null;
        private Task downloadTask = null;

        public FileDownload()
        {
            InitializeComponent();
        }

        public FileDownload(string fileUri)
            : this()
        {
            this.fileUri = new Uri(fileUri);
        }

        public void Download()
        {
            btnCancel.Text = "&Cancel";
            btnCancel.Enabled = false;
            progressBar.Style = ProgressBarStyle.Marquee;
            string filename = Path.GetFileName(fileUri.AbsolutePath);
            lblFileName.Text = filename;
            lblUri.Text = fileUri.AbsoluteUri;

            FileDownloader downloader = new FileDownloader(fileUri);
            FileDownloader filesizer = new FileDownloader(fileUri);
            filesizer.ReqState.ResponseInfoCallback = new ResponseInfoDelegate(SetResponseInfo);
            downloader.ReqState.ResponseInfoCallback = new ResponseInfoDelegate(SetResponseInfo);
            downloader.ReqState.ProgressCallback = new ProgressDelegate(Progress);
            downloader.ReqState.DoneCallback = new DoneDelegate(Done);
            downloadTask = Task.Factory.StartNew(() => downloader.AsyncDownload());
            filesizeTask = Task.Factory.StartNew<long>(() => filesizer.GetFileSizeFromDirectoryDetails());
            filesizeTask.ContinueWith((contentLength) =>
                {
                    SetResponseInfo(string.Empty, contentLength.Result);
                    downloader.ReqState.TotalBytes = contentLength.Result;
                });
        }

        /// <summary>
        /// Response info callback, called after we get HTTP response header.
        /// Used to set info in GUI about max download size.
        /// </summary>
        private void SetResponseInfo(string statusDescription, long contentLength)
        {
            if (this.InvokeRequired)
            {
                ResponseInfoDelegate del = new ResponseInfoDelegate(SetResponseInfo);
                Invoke(del, new object[] { statusDescription, contentLength });
            }
            else
            {
                //lblStatusDescr.Text = statusDescr;
                if (contentLength > 0)
                    lblHeader.Text = string.Format("Downloading {0} file{1} ({2})", "1", string.Empty, FormatFileSize(contentLength));
                else
                    lblHeader.Text = string.Format("Downloading {0} file{1} (Unable to determine filesize)", "1", string.Empty);
                btnCancel.Enabled = true;
                progressBar.Style = ProgressBarStyle.Continuous;
            }
        }

        enum Units { KB, MB, GB, TB }

        private string FormatFileSize(long size)
        {
            int index = 0;
            double bytes = size;
            while (bytes > 1024)
            {
                bytes = bytes / 1024;
                index++;
            }

            return string.Format("{0} {1}", bytes.ToString("F"), Units.KB + index - 1);
        }

        /// <summary>
        /// Progress callback, called when we've read another packet of data.
        /// Used to set info in GUI on % complete & transfer rate.
        /// </summary>
        private void Progress(int totalBytes, double percentComplete, double transferRate)
        {
            if (this.InvokeRequired)
            {
                ProgressDelegate del = new ProgressDelegate(Progress);
                Invoke(del, new object[] { totalBytes, percentComplete, transferRate });
            }
            else
            {
                lblTimeRemaining.Text = totalBytes.ToString();
                progressBar.Value = (int)percentComplete;
                lblSpeed.Text = string.Format("{0} kB/sec", transferRate.ToString("f0"));
            }
        }

        /// <summary>
        /// GUI-updating callback called when download has completed.
        /// </summary>
        private void Done()
        {
            if (this.InvokeRequired)
            {
                DoneDelegate del = new DoneDelegate(Done);
                Invoke(del, new object[] { });
            }
            else
            {
                progressBar.Value = 100;
                progressBar.Style = ProgressBarStyle.Marquee;
                if (Path.GetFileName(fileUri.AbsolutePath).EndsWith(".gz")) DecompressFile();
                progressBar.Style = ProgressBarStyle.Continuous;
                lblHeader.Text = "Download completed!";
                btnCancel.Text = "&Close";
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (btnCancel.Text.Contains("Close")) this.Close();
        }

        private void DecompressFile()
        {
            if (this.InvokeRequired)
            {
                Invoke(new MethodInvoker(DecompressFile));
            }
            else
            {
                string filename = Path.GetFileName(fileUri.AbsolutePath);
                if (!File.Exists(filename)) return;
                lblHeader.Text = "Decompressing zipped file...";
                FileDownloader.DecompressFile(filename);
            }
        }
    }
}
